﻿using System;
using System.Net;
using System.IO;
using System.Text;

/*
 * According to Google's Secret API
 */ 
public class GoogleAPI
{
    private static bool isDebug = false;   // option for debug

    /*
     * Change integer to 4 bytes, with little endian
     */
    private static void FillByte(byte[] byteArray, int offset, int data)
    {
        byteArray[offset]   = (byte)((data >> 24) & 0xFF);
        byteArray[offset+1] = (byte)((data >> 16) & 0xFF);
        byteArray[offset+2] = (byte)((data >> 8)  & 0xFF);
        byteArray[offset+3] = (byte)((data >> 0)  & 0xFF);
    }

    /*
     * Use CID, LAC ,MCC and MNC
     */
    public static bool GetLatLng(int CID, int LAC, int MCC, int MNC, 
                                 out double lat, out double lng)
    {
        String url = "http://www.google.com/glm/mmap";
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
            new Uri(url));
        req.Method = "POST";
        byte[] postData = { 0x00, 0x0e,  
                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  
                            0x00, 0x00,  
                            0x00, 0x00,  
                            0x00, 0x00,  
 
                            0x1b,  
                            0x00, 0x00, 0x00, 0x00, // offset 0x11, MNC  
                            0x00, 0x00, 0x00, 0x00, // offset 0x15, MCC  
                            0x00, 0x00, 0x00, 0x00, // offset 0x19  
                            0x00, 0x00,  
                            0x00, 0x00, 0x00, 0x00, // offset 0x1f, MNC  
                            0x00, 0x00, 0x00, 0x00, // offset 0x23, MCC  
                            0x00, 0x00, 0x00, 0x00, // offset 0x27, CID  
                            0x00, 0x00, 0x00, 0x00, // offset 0x2b, LAC  
                            0xff, 0xff, 0xff, 0xff,  
                            0x00, 0x00, 0x00, 0x00 };
        
        // UTMS: 6 hex digits, GSM: 4 hex digits
        if ((Int64)CID > 65536)
            postData[0x1c] = 5;
        else
            postData[0x1c] = 3;
        
        if (isDebug)
             Console.WriteLine("{0} CID.", ((Int64)CID > 65536) ? "UMTS" : "GSM");

            
        // Fill parameters
        FillByte(postData, 0x11, MNC);
        FillByte(postData, 0x15, MCC);
        FillByte(postData, 0x27, MNC);
        FillByte(postData, 0x2b, MCC);
        FillByte(postData, 0x1f, CID);
        FillByte(postData, 0x23, LAC);

        req.ContentLength = postData.Length;
        req.ContentType = "application/binary";
        
        Stream outputStream = req.GetRequestStream();
        outputStream.Write(postData, 0, postData.Length);
        outputStream.Close();

        //Get response
        HttpWebResponse res = (HttpWebResponse)req.GetResponse();
        byte[] tmpBytes = new byte[res.ContentLength];
        int totalBytesRead = 0;
        while (totalBytesRead < tmpBytes.Length)
        {
            totalBytesRead += res.GetResponseStream().Read(
                tmpBytes, totalBytesRead, tmpBytes.Length - totalBytesRead);
        }

        if (res.StatusCode == HttpStatusCode.OK)
        {
            int ret_code = (int)((tmpBytes[3] << 24) | (tmpBytes[4] << 16) 
                | (tmpBytes[5] << 8) | (tmpBytes[6]));
            if (ret_code == 0)
            {
                lat = ((double)((tmpBytes[7] << 24) | (tmpBytes[8] << 16) 
                    | (tmpBytes[9] << 8) | (tmpBytes[10]))) / 1000000;
                lng = ((double)((tmpBytes[11] << 24) | (tmpBytes[12] << 16) 
                    | (tmpBytes[13] << 8) | (tmpBytes[14]))) / 1000000;
                return true;
            }
        }
        
        lat = 0;
        lng = 0;
        return false;      
    }

    /*
     * Just use CID and LAC
     */ 
    public static bool GetLatLng(int CID, int LAC, out double lat, out double lng)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(
            "http://www.google.com/glm/mmap");
        request.Method = "POST";

        byte[] byteArray = { 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 
                             0x65, 0x6E,       // "en"
                             0x00, 0x07,
                             0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, // "Android" 
                             0x00, 0x03, 
                             0x31, 0x2E, 0x30, // "1.0"
                             0x00, 0x03, 
                             0x57, 0x65, 0x62, // "web"
                             0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                             0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 
                             0xFF, 0xFF, 0xFF, 0xFF, // here's CID
                             0xFF, 0xFF, 0xFF, 0xFF, // here's LAC
                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        
        //Write CellID and LAC
        FillByte(byteArray, 48, CID);
        FillByte(byteArray, 52, LAC);

        //Set request
        request.ContentLength = byteArray.Length;
        Stream postStream = request.GetRequestStream();
        postStream.Write(byteArray, 0, byteArray.Length);
        postStream.Close();

        //Get response
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        if (isDebug)
        {
            Console.WriteLine("Request responses: {0}", response.StatusDescription);
        }

        //Read response
        Stream dataStream = response.GetResponseStream();
        BinaryReader br = new BinaryReader(dataStream);

        //Skip 3 bytes
        br.ReadByte();
        br.ReadByte();
        br.ReadByte();

        //Check state
        if (0 == br.ReadInt32())
        {
            //Read lat
            byte[] tmpBytes = new byte[4];
            tmpBytes[3] = br.ReadByte();
            tmpBytes[2] = br.ReadByte();
            tmpBytes[1] = br.ReadByte();
            tmpBytes[0] = br.ReadByte();
            lat = (double)(BitConverter.ToInt32(tmpBytes, 0)) / 1000000;

            //Read lng
            tmpBytes[3] = br.ReadByte();
            tmpBytes[2] = br.ReadByte();
            tmpBytes[1] = br.ReadByte();
            tmpBytes[0] = br.ReadByte();
            lng = (double)(BitConverter.ToInt32(tmpBytes, 0)) / 1000000;

            br.Close();
            dataStream.Close();
            response.Close();
            return true;
        }
        else
        {
            br.Close();
            dataStream.Close();
            response.Close();
            lat = 0;
            lng = 0;
            return false;
        }
    }
}